<template>
	<view class="v-image-container" @click="handleClick">
		<!-- 根据环境选择不同的标签 -->
		<template v-if="!isError">
			<image
				v-if="isAppPlus"
				ref="imgElement"
				:src="finalSrc"
				:class="['v-image', `v-image--${model}`, { 'v-image--loading': isLoading, 'v-image--disabled': disabled }]"
				@click="handlePreview"
				@load="handleLoad"
				@error="handleError"
			/>
			<img
				v-else
				ref="imgElement"
				:src="finalSrc"
				:class="['v-image', `v-image--${model}`, { 'v-image--loading': isLoading, 'v-image--disabled': disabled }]"
				@click="handlePreview"
				@load="handleLoad"
				@error="handleError"
			/>
		</template>

		<slot name="loading" v-if="isLoading">
			<view class="v-image-loading">
				<v-loading text="加载中……" show />
			</view>
		</slot>
		<slot name="error" v-if="isError">
			<view class="v-image-error">
				<text class="error-text">加载失败</text>
			</view>
		</slot>
	</view>
	<v-overlay v-model:value="showPreview" @click="closePreview" v-if="showPreview">
		<image v-if="isAppPlus" :src="finalPreviewSrc" :class="['v-image-preview__img', `v-image-preview__img--${previewModel}`]" @click.stop />
		<img v-else :src="finalPreviewSrc" :class="['v-image-preview__img', `v-image-preview__img--${previewModel}`]" @click.stop />
	</v-overlay>
</template>

<script lang="ts" setup>
import { ref, watch, onMounted, onUnmounted, inject, computed } from 'vue';

const props = defineProps({
	src: {
		type: String,
		default: '',
		required: true
	},
	width: {
		type: String,
		default: '100%'
	},
	height: {
		type: String,
		default: '100%'
	},
	model: {
		type: String,
		default: 'square',
		validator: (val: string) => ['circle', 'rounded', 'square'].includes(val)
	},
	borderRadius: {
		type: String,
		default: '10rpx'
	},
	previewWidth: {
		type: String,
		default: 'auto'
	},
	previewHeight: {
		type: String,
		default: 'auto'
	},
	enablePreview: {
		type: Boolean,
		default: false
	},
	previewModel: {
		type: String,
		default: 'square',
		validator: (val: string) => ['circle', 'rounded', 'square'].includes(val)
	},
	previewBorderRadius: {
		type: String,
		default: '10rpx'
	},
	loading: {
		type: Boolean,
		default: false
	},
	disabled: {
		type: Boolean,
		default: false
	},
	lazy: {
		type: Boolean,
		default: false
	},
	threshold: {
		type: Number,
		default: 0.1
	},
	delay: {
		type: Number,
		default: 800
	},
	isLoading: {
		type: Boolean,
		default: false
	},
	retryCount: {
		type: Number,
		default: 3
	}
});

const emit = defineEmits(['update:enablePreview', 'update:loading', 'handleLoading', 'handError', 'click']);

const config = inject<any>('config');
const showPreview = ref(false);
const isLoading = ref(props.isLoading);
const isError = ref(false);
const imgElement = ref<HTMLElement | null>(null);
const displaySrc = ref<string | null>(null);

let lazyLoadTimer: any = null;
let scrollHandler: any = null;
const retryCount = ref(0);
const maxRetryCount = ref(props.retryCount);

// 判断是否是 App 环境
const isAppPlus = ref(false);

// 判断是否是 HTTP 或 HTTPS 链接
const isHttpOrHttps = (url: string) => {
	return /^https?:\/\//.test(url);
};

// 动态计算图片路径
const finalSrc = computed(() => {
	if (!props.src) return '';
	return isHttpOrHttps(props.src) ? props.src : config.VImage.prefix + props.src;
});

const finalPreviewSrc = computed(() => {
	if (!props.src) return '';
	return isHttpOrHttps(props.src) ? props.src : config.VImage.prefix + props.src;
});

onMounted(() => {
	// 判断当前环境
	if (process.env.VUE_APP_PLATFORM === 'app-plus') {
		isAppPlus.value = true;
	} else {
		isAppPlus.value = false;
	}

	if (props.lazy && props.src && window) {
		window.addEventListener('scroll', handleScroll);
		handleScroll();
	} else {
		displaySrc.value = finalSrc.value;
	}
});

const handleScroll = () => {
	if (!props.lazy || !props.src || !imgElement.value) return;

	const rect = imgElement.value.getBoundingClientRect();
	const isVisible = rect.top < (window.innerHeight || document.documentElement.clientHeight) && rect.bottom > 0;

	if (isVisible) {
		loadImage();
	}
};

const loadImage = () => {
	clearTimeout(lazyLoadTimer);
	lazyLoadTimer = setTimeout(() => {
		displaySrc.value = finalSrc.value;
		if (displaySrc.value && displaySrc.value.trim()) {
			isHttpOrHttps(props.src) ? isLoading.value = false : isLoading.value = true
			isError.value = false;
		}
	}, props.delay);
};

const handleLoad = () => {
	isLoading.value = false;
	emit('handleLoading');
	retryCount.value = 0; // 成功后重置重试计数
};

const handleRetry = () => {
	if (retryCount.value < maxRetryCount.value) {
		retryCount.value++;
		displaySrc.value = null;
		isLoading.value = true;
		isError.value = false;
		loadImage();
		emit('handError', retryCount.value);
	} else {
		emit('handError', retryCount.value);
	}
};

const handleError = () => {
	isLoading.value = false;
	isError.value = true;
	handleRetry();
};

const handlePreview = () => {
	if (props.enablePreview && props.src && !props.disabled) {
		showPreview.value = true;
	}
};

const handleClick = () => {
	emit('click');
};

const closePreview = () => {
	showPreview.value = false;
};

watch(
	() => showPreview.value,
	(newValue) => {
		emit('update:enablePreview', newValue);
	}
);

watch(
	() => isLoading.value,
	() => {
		emit('update:loading', isLoading.value);
	}
);

watch(
	() => props.src,
	(newSrc) => {
		if (!props.lazy) {
			displaySrc.value = finalSrc.value;
			if (newSrc) {
				isLoading.value = true;
				isError.value = false;
			}
		} else {
			if (imgElement.value && window) {
				loadImage();
			}
		}
	}
);

onUnmounted(() => {
	clearTimeout(lazyLoadTimer);
	if (window) {
		window.removeEventListener('scroll', handleScroll);
	}
});
</script>

<style lang="scss" scoped>
.v-image-container {
	width: 100%;
	height: 100%;
	display: flex;
	justify-content: center;
	align-items: center;
	position: relative;
}

.v-image {
	max-width: 100%;
	max-height: 100%;
	width: v-bind('props.width');
	height: v-bind('props.height');
	object-fit: cover;

	&--circle {
		border-radius: 50%;
	}

	&--rounded {
		border-radius: v-bind('props.borderRadius');
	}

	&--square {
		border-radius: 0;
	}

	&--loading,
	&--disabled {
		opacity: v-bind('config.opacity.disabled');
	}
}

.v-image-preview__img {
	max-width: 90%;
	max-height: 90%;
	object-fit: contain;

	&--circle {
		border-radius: 50%;
	}

	&--rounded {
		border-radius: v-bind('props.previewBorderRadius');
	}

	&--square {
		border-radius: 0;
	}
}

.v-image-loading {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	display: flex;
	justify-content: center;
	align-items: center;
	z-index: 1;
}

.v-image-error {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	z-index: 1;

	.error-text {
		font-size: v-bind('config.fontSize.smallText');
		color: v-bind('config.fontColor.subTitle');
		margin-top: 12rpx;
	}
}
</style>
